2 * Adium is the legal property of its developers, whose names are listed in the copyright file included
3 * with this source distribution.
5 * This program is free software; you can redistribute it and/or modify it under the terms of the GNU
6 * General Public License as published by the Free Software Foundation; either version 2 of the License,
7 * or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
10 * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
11 * Public License for more details.
13 * You should have received a copy of the GNU General Public License along with this program; if not,
14 * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17 #import "AIListCell.h"
18 #import "AIListOutlineView.h"
19 #import <AIUtilities/AIWindowAdditions.h>
20 #import <AIUtilities/CBApplicationAdditions.h>
21 #import <AIUtilities/ESOutlineViewAdditions.h>
23 #define MINIMUM_HEIGHT 48
24 #define MINIMUM_WIDTH 140
26 @interface AIListOutlineView (PRIVATE)
27 - (void)_initListOutlineView;
30 @implementation AIListOutlineView
32 - (id)initWithCoder:(NSCoder *)aDecoder
34 [super initWithCoder:aDecoder];
35 [self _initListOutlineView];
39 - (id)initWithFrame:(NSRect)frame
41 [super initWithFrame:frame];
42 [self _initListOutlineView];
47 - (void)_initListOutlineView
49 updateShadowsWhileDrawing = NO;
51 backgroundImage = nil;
53 backgroundColor = nil;
54 backgroundStyle = AINormalBackground;
56 [self sizeLastColumnToFit];
61 [backgroundImage release];
62 [backgroundColor release];
67 //Prevent the display of a focus ring around the contact list in 10.3 and greater
68 - (NSFocusRingType)focusRingType
70 return NSFocusRingTypeNone;
73 //When our delegate is set, ask it for our data cells
74 - (void)setDelegate:(id)delegate
76 [super setDelegate:delegate];
79 //Keep our column full width
80 - (void)setFrameSize:(NSSize)newSize
82 [super setFrameSize:newSize];
83 [self sizeLastColumnToFit];
87 //Selection Hiding -----------------------------------------------------------------------------------------------------
88 //If our window isn't in the foreground, we're not displaying a selection. So override this method to pass NO for
89 //selected in that situation
90 - (void)_drawRowInRect:(NSRect)rect colored:(BOOL)colored selected:(BOOL)selected
92 if(![[self window] isKeyWindow]) selected = NO;
93 [super _drawRowInRect:rect colored:colored selected:selected];
96 //When our view is inserted into a window, observe that window so we can hide selection when it's not main
97 - (void)configureSelectionHidingForNewSuperview:(NSView *)newSuperview
99 [[NSNotificationCenter defaultCenter] removeObserver:self name:NSWindowDidBecomeMainNotification object:[self window]];
100 [[NSNotificationCenter defaultCenter] removeObserver:self name:NSWindowDidResignMainNotification object:[self window]];
101 if([newSuperview window]){
102 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(windowBecameMain:) name:NSWindowDidBecomeMainNotification object:[newSuperview window]];
103 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(windowResignedMain:) name:NSWindowDidResignMainNotification object:[newSuperview window]];
107 //Redraw our cells so they can select or de-select
108 - (void)windowBecameMain:(NSNotification *)notification{
109 [self setNeedsDisplay:YES];
111 - (void)windowResignedMain:(NSNotification *)notification{
112 [self setNeedsDisplay:YES];
116 - (void)cancelOperation:(id)sender
118 [self deselectAll:nil];
121 //Sizing -----------------------------------------------------------------------------------------------------
122 // Returns our desired size
125 int desiredHeight = [self totalHeight] + desiredHeightPadding;
126 return desiredHeight > MINIMUM_HEIGHT ? desiredHeight : MINIMUM_HEIGHT;
132 unsigned numberOfRows = [self numberOfRows];
134 id theDelegate = [self delegate];
136 //Enumerate all rows, find the widest one
137 for(row = 0; row < numberOfRows; row++){
138 id item = [self itemAtRow:row];
139 NSCell *cell = ([self isExpandable:item] ? groupCell : contentCell);
141 [theDelegate outlineView:self willDisplayCell:cell forTableColumn:nil item:item];
142 int width = [(AIListCell *)cell cellWidth];
143 if(width > widestCell) widestCell = width;
146 return ((widestCell > MINIMUM_WIDTH) || ignoreMinimumWidth) ? widestCell : MINIMUM_WIDTH;
149 - (void)setIgnoreMinimumWidth:(BOOL)inFlag
151 ignoreMinimumWidth = inFlag;
154 //Add padding to the desired height
155 - (void)setDesiredHeightPadding:(int)inPadding
157 desiredHeightPadding = inPadding;
161 //Background image ---------------------------------------------------------------
162 //Draw our background image or color with transparency
163 - (void)drawBackgroundInClipRect:(NSRect)clipRect
165 if([self drawsBackground]){
167 [[self backgroundColor] set];
168 NSRectFill(clipRect);
171 NSScrollView *enclosingScrollView = [self enclosingScrollView];
172 if(backgroundImage && enclosingScrollView){
173 NSRect visRect = [enclosingScrollView documentVisibleRect];
174 NSSize imageSize = [backgroundImage size];
175 NSRect imageRect = NSMakeRect(0.0, 0.0, imageSize.width, imageSize.height);
177 switch(backgroundStyle){
179 case AINormalBackground:{
180 //Background image normal
181 [backgroundImage drawInRect:NSMakeRect(visRect.origin.x, visRect.origin.y, imageSize.width, imageSize.height)
183 operation:NSCompositeSourceOver
184 fraction:backgroundFade];
187 case AIFillProportionatelyBackground:{
188 //Background image proportional stretch
190 //Make the width change by the same proportion as the height will change
191 //visRect.size.width = imageSize.width * (visRect.size.height / imageSize.height);
193 //Make the height change by the same proportion as the width will change
194 visRect.size.height = imageSize.height * (visRect.size.width / imageSize.width);
196 //Background image stretch
197 [backgroundImage drawInRect:visRect
199 operation:NSCompositeSourceOver
200 fraction:backgroundFade];
203 case AIFillStretchBackground:{
204 //Background image stretch
205 [backgroundImage drawInRect:visRect
207 operation:NSCompositeSourceOver
208 fraction:backgroundFade];
211 case AITileBackground:{
213 NSPoint currentOrigin;
214 currentOrigin = visRect.origin;
216 //We'll repeat this vertical process as long as necessary
217 while (currentOrigin.y < (visRect.origin.y + visRect.size.height)) {
218 //Reset the x axis to draw a series of images horizontally at this height
219 currentOrigin.x = visRect.origin.x;
221 //Draw as long as our origin is within the visible rect
222 while (currentOrigin.x < (visRect.origin.x + visRect.size.width)) {
223 //Draw at the current x and y at least once with the original size
224 [backgroundImage drawInRect:NSMakeRect(currentOrigin.x, currentOrigin.y, imageSize.width, imageSize.height)
226 operation:NSCompositeSourceOver
227 fraction:backgroundFade];
229 //Shift right for the next iteration
230 currentOrigin.x += imageSize.width;
233 //Shift down for the next series of horizontal draws
234 currentOrigin.y += imageSize.height;
242 //If we aren't drawing a background, fill the rect with clearColor
243 [[NSColor clearColor] set];
244 NSRectFill(clipRect);
248 //Background -----------------------------------------------------------------
250 - (void)setBackgroundImage:(NSImage *)inImage
252 if(backgroundImage != inImage){
253 [backgroundImage release];
254 backgroundImage = [inImage retain];
255 [backgroundImage setFlipped:YES];
258 [(NSClipView *)[self superview] setCopiesOnScroll:(!backgroundImage)];
259 [self setNeedsDisplay:YES];
262 - (void)setBackgroundStyle:(AIBackgroundStyle)inBackgroundStyle
264 backgroundStyle = inBackgroundStyle;
265 [self setNeedsDisplay:YES];
269 - (void)setBackgroundOpacity:(float)opacity forWindowStyle:(LIST_WINDOW_STYLE)windowStyle
271 backgroundOpacity = opacity;
273 //Reset all our opacity dependent values
274 [_backgroundColorWithOpacity release]; _backgroundColorWithOpacity = nil;
275 [_rowColorWithOpacity release]; _rowColorWithOpacity = nil;
277 //Turn our shadow drawing hack on if they're going to be visible through the transparency
278 [self setUpdateShadowsWhileDrawing:((backgroundOpacity < 0.9) ||
279 (windowStyle == WINDOW_STYLE_PILLOWS_FITTED))];
281 //Mockie and pillow lists always require a non-opaque window, other lists only require a non-opaque window when
282 //the user has requested transparency.
283 if(windowStyle == WINDOW_STYLE_MOCKIE || windowStyle == WINDOW_STYLE_PILLOWS || windowStyle == WINDOW_STYLE_PILLOWS_FITTED){
284 [[self window] setOpaque:NO];
286 [[self window] setOpaque:(backgroundOpacity == 1.0)];
289 [self setNeedsDisplay:YES];
292 - (void)setBackgroundFade:(float)fade
294 backgroundFade = fade;
295 [self setNeedsDisplay:YES];
297 - (float)backgroundFade
300 return backgroundFade * backgroundOpacity;
303 //Background color (Opacity is added into the return automatically)
304 - (void)setBackgroundColor:(NSColor *)inColor
306 if(backgroundColor != inColor){
307 [backgroundColor release];
308 backgroundColor = [inColor retain];
309 [_backgroundColorWithOpacity release];
310 _backgroundColorWithOpacity = nil;
312 [self setNeedsDisplay:YES];
314 - (NSColor *)backgroundColor
317 if(!_backgroundColorWithOpacity){
318 _backgroundColorWithOpacity = [[backgroundColor colorWithAlphaComponent:backgroundOpacity] retain];
321 return _backgroundColorWithOpacity;
324 //Alternating row color (Opacity is added into the return automatically)
325 - (void)setAlternatingRowColor:(NSColor *)color
327 if(rowColor != color){
329 rowColor = [color retain];
330 [_rowColorWithOpacity release];
331 _rowColorWithOpacity = nil;
334 [self setNeedsDisplay:YES];
337 - (NSColor *)alternatingRowColor
339 if(!_rowColorWithOpacity){
340 _rowColorWithOpacity = [[rowColor colorWithAlphaComponent:backgroundOpacity] retain];
343 return _rowColorWithOpacity;
346 - (void)viewWillMoveToSuperview:(NSView *)newSuperview
348 [super viewWillMoveToSuperview:newSuperview];
350 [(NSClipView *)newSuperview setCopiesOnScroll:(!backgroundImage)];
353 /* ######################## Crappy code alert ########################
354 * Drawing the background image/color should be as simple as subclassing
355 * drawBackgroundInClipRect: but that method is only called in 10.3 and
356 * we need 10.2 compatibility.
358 * We need to get called after the background draws, but before the rows
359 * start drawing. A crappy solution is to draw our background right
360 * before the outline view tries to draw its first row. We only need to
361 * do this when running in 10.2
363 - (void)drawRect:(NSRect)rect
365 if(![NSApp isOnPantherOrBetter]) _drawBackground = YES;
366 [super drawRect:rect];
368 /* #################### More Crappy Code ###################
369 * This time for 10.3 compatibility. 10.3 does NOT invalidate the shadow
370 * of a transparent window correctly, forcing us to do it manually each
371 * time the window content is changed. This is absolutely horrible for
372 * performance, but the only way to avoid shadow ghosting in 10.3 :(
374 if(updateShadowsWhileDrawing) [[self window] invalidateShadow];
376 - (void)drawRow:(int)row clipRect:(NSRect)rect
379 _drawBackground = NO;
380 [self drawBackgroundInClipRect:[self frame]];
382 [super drawRow:row clipRect:rect];
384 - (void)setUpdateShadowsWhileDrawing:(BOOL)update{
385 updateShadowsWhileDrawing = update;
387 // ###################################################################
391 //Contact menu ---------------------------------------------------------------
392 //Return the selected object (to auto-configure the contact menu)
393 - (AIListObject *)listObject
395 int selectedRow = [self selectedRow];
397 if(selectedRow >= 0 && selectedRow < [self numberOfRows]){
398 return [self itemAtRow:selectedRow];
404 - (NSArray *)arrayOfListObjects
406 return [self arrayOfSelectedItems];